package org.jeecg.common.wx;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.modules.doctor.entity.Doctor;
import org.jeecg.common.modules.doctor.entity.DoctorConfig;
import org.jeecg.common.modules.doctor.entity.DoctorWxUserinfoRelation;
import org.jeecg.common.modules.doctor.service.IDoctorConfigService;
import org.jeecg.common.modules.doctor.service.IDoctorService;
import org.jeecg.common.modules.doctor.service.IDoctorWxUserinfoRelationService;
import org.jeecg.common.modules.order.entity.OrderInfo;
import org.jeecg.common.modules.order.entity.PayOrder;
import org.jeecg.common.modules.order.service.IOrderInfoService;
import org.jeecg.common.modules.order.service.IPayOrderService;
import org.jeecg.common.modules.register.entity.RegisterPayOrder;
import org.jeecg.common.modules.register.service.IRegisterPayOrderService;
import org.jeecg.common.modules.wx.entity.WxUserinfo;
import org.jeecg.common.modules.wx.service.IWxUserinfoService;
import org.jeecg.common.util.DySmsEnum;
import org.jeecg.common.util.DySmsHelper;
import org.jeecg.common.util.enums.OrderStatusEnum;
import org.jeecg.common.util.enums.OrderTypeEnum;
import org.jeecg.common.util.enums.PayOrderStatusEnum;
import org.jeecg.common.util.result.Result;
import org.jeecg.common.util.result.ResultMapper;
import org.jeecg.common.wx.request.WxRefundRequestVo;
import org.jeecg.common.wx.response.WxRefundResponseVo;
import org.springframework.cache.support.NullValue;
import org.springframework.stereotype.Service;
import org.weixin4j.pay.WeixinPayException;
import org.weixin4j.spring.WeixinTemplate;
import org.weixin4j.util.SignUtil;

import javax.annotation.Resource;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.StringReader;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

import static org.jeecg.common.util.constants.WxConstants.SUCCESS;

/**
 * @author guoyh
 * @ClassName WxRefundService
 * @Description
 * @since 2020/9/3 01:08
 **/
@Service
@Slf4j
public class WxRefundService {

    @Resource
    private IPayOrderService payOrderService;
    @Resource
    private IOrderInfoService orderService;
    @Resource
    private IDoctorService doctorService;

    @Resource
    private WeixinTemplate weixinTemplate;

    @Resource
    private IWxUserinfoService wxUserinfoService;
    @Resource
    private IDoctorWxUserinfoRelationService doctorWxUserinfoRelationService;
    @Resource
    private IDoctorConfigService doctorConfigService;
    @Resource
    private IRegisterPayOrderService registerPayOrderService;

    public Result<NullValue> refund(PayOrder refundOrder, String wxOrderCode,OrderInfo orderInfo,PayOrder payOrder,String reason) throws WeixinPayException{
        WxRefundRequestVo wxRefundRequestVo = buidWxRefundRequest(refundOrder,wxOrderCode);
        WxRefundResponseVo responseVo = doRefund(wxRefundRequestVo);
        return handleRefundResponse(responseVo,refundOrder,orderInfo,payOrder,reason);
    }
    public void refundNew(String amount, String wxOrderCode) throws WeixinPayException{
        WxRefundRequestVo wxRefundRequestVo = buidWxRefundRequestNew(amount,wxOrderCode);
         doRefund(wxRefundRequestVo);
    }

    private WxRefundRequestVo buidWxRefundRequestNew(String amount, String wxOrderCode){
        String outRefundCode = IdUtil.createSnowflake(1,1).nextIdStr();
        String totalFee = amount;
        String refundFee = totalFee;
        WxRefundRequestVo refundRequestVo = new WxRefundRequestVo();
        refundRequestVo.setAppid(weixinTemplate.getWeixinConfig().getAppid());
        refundRequestVo.setMch_id(weixinTemplate.getWeixinPayConfig().getPartnerId());
        refundRequestVo.setNonce_str(IdUtil.createSnowflake(1,1).nextIdStr());
        refundRequestVo.setTransaction_id(wxOrderCode);
        refundRequestVo.setOut_refund_no(outRefundCode);
        refundRequestVo.setTotal_fee(totalFee);
        refundRequestVo.setRefund_fee(refundFee);
        Map<String,String> map = refundRequestVo.toMap();
        String sign = SignUtil.getSign(map,weixinTemplate.getWeixinPayConfig().getPartnerKey());
        refundRequestVo.setSign(sign);
        return refundRequestVo;
    }
    private WxRefundRequestVo buidWxRefundRequest(PayOrder refundOrder, String wxOrderCode){
        String outRefundCode = IdUtil.createSnowflake(1,1).nextIdStr();
        String totalFee = refundOrder.getAmount().toString();
        String refundFee = totalFee;
        WxRefundRequestVo refundRequestVo = new WxRefundRequestVo();
        refundRequestVo.setAppid(weixinTemplate.getWeixinConfig().getAppid());
        refundRequestVo.setMch_id(weixinTemplate.getWeixinPayConfig().getPartnerId());
        refundRequestVo.setNonce_str(IdUtil.createSnowflake(1,1).nextIdStr());
        refundRequestVo.setTransaction_id(wxOrderCode);
        refundRequestVo.setOut_refund_no(outRefundCode);
        refundRequestVo.setTotal_fee(totalFee);
        refundRequestVo.setRefund_fee(refundFee);
        Map<String,String> map = refundRequestVo.toMap();
        String sign = SignUtil.getSign(map,weixinTemplate.getWeixinPayConfig().getPartnerKey());
        refundRequestVo.setSign(sign);
        return refundRequestVo;
    }


    private WxRefundResponseVo doRefund(WxRefundRequestVo refundRequestVo) throws WeixinPayException {
        String xmlPost = refundRequestVo.toXML();
        log.info("微信退款，提交XML数据：{}",xmlPost);
        org.weixin4j.pay.http.HttpsClient httpsClient = new org.weixin4j.pay.http.HttpsClient();
        org.weixin4j.pay.http.Response response = httpsClient.postXmlWithCert("https://api.mch.weixin.qq.com/secapi/pay/refund", xmlPost, weixinTemplate.getWeixinPayConfig().getPartnerId(), weixinTemplate.getWeixinPayConfig().getCertPath(), weixinTemplate.getWeixinPayConfig().getCertSecret());
        String xmlResult = response.asString();
        log.info("微信退款，返回：{}",xmlResult);
        try {
            //创建XML解析对象
            JAXBContext context = JAXBContext.newInstance(WxRefundResponseVo.class);
            XMLInputFactory xif = XMLInputFactory.newFactory();
            xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
            xif.setProperty(XMLInputFactory.SUPPORT_DTD, true);
            //防XXE攻击
            XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(xmlResult));
            Unmarshaller unmarshaller = context.createUnmarshaller();
            //解析XML对象
            WxRefundResponseVo rsaXml = (WxRefundResponseVo) unmarshaller.unmarshal(xsr);
            log.info("{}",rsaXml);
            return rsaXml;
        } catch (JAXBException | XMLStreamException ex) {
            log.error("微信退款，解析XML异常");
        }
        return null;
    }

    private Result<NullValue> handleRefundResponse(WxRefundResponseVo refundResponseVo, PayOrder refundOrder, OrderInfo orderInfo, PayOrder payOrder,String reason){
        if (Objects.isNull(refundResponseVo)){
            throw new RuntimeException("获取退款结果失败");
        }
        if (!StringUtils.equals(refundResponseVo.getReturn_code(),SUCCESS)){
            throw new RuntimeException(refundResponseVo.getReturn_msg());
        }
        if (!StringUtils.equals(refundResponseVo.getResult_code(),SUCCESS)){
            throw new RuntimeException(refundResponseVo.getErr_code_des());
        }
        refundOrder.setThirdOrderCode(refundResponseVo.getTransaction_id());
        refundOrder.setEndPayTime(new Date());
        refundOrder.setStatus(PayOrderStatusEnum.SUCCESS.getCode());
        payOrderService.updateById(refundOrder);
        payOrder.setStatus(PayOrderStatusEnum.REFUND.getCode());
        payOrderService.updateById(payOrder);
        orderInfo.setStatus(OrderStatusEnum.CLOSE.getCode());
        orderInfo.setUpdateTime(new Date());
        orderInfo.setReason(reason);
        orderService.updateById(orderInfo);
        OrderTypeEnum orderType = OrderTypeEnum.getInstance(orderInfo.getShopType());
        WxUserinfo wxUserinfo = wxUserinfoService.getById(orderInfo.getWxUserinfoId());
        Doctor doctor = doctorService.getById(orderInfo.getDoctorId());
        DoctorWxUserinfoRelation relation = doctorWxUserinfoRelationService.getByDoctorAndOpenid(doctor.getId(),wxUserinfo.getOpenid());
        if (orderType.equals(OrderTypeEnum.VIDEO)){
            relation.setVideoNumber(0);
        }else{
            DoctorConfig doctorConfig = doctorConfigService.getByDoctorId(doctor.getId(),false);
            relation.setTextNumber(doctorConfig.getFreeMessageCount());
        }
        doctorWxUserinfoRelationService.updateById(relation);

        try {

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("orderId",orderInfo.getId());
            jsonObject.put("amount",orderInfo.getAmount().toPlainString());
            DySmsHelper.sendSms(doctor.getMobile(),jsonObject, DySmsEnum.REFUND);
        } catch (Exception e) {
            log.error("发送退款短信失败,doctorId{},orderId:{}",orderInfo.getId(),orderInfo.getId(),e);
        }
        return ResultMapper.ok();
    }


    private Result<NullValue> handleRefundResponseNew(WxRefundResponseVo refundResponseVo, PayOrder refundOrder, RegisterPayOrder payOrderInfo, PayOrder payOrder,String reason){
        if (Objects.isNull(refundResponseVo)){
            throw new RuntimeException("获取退款结果失败");
        }
        if (!StringUtils.equals(refundResponseVo.getReturn_code(),SUCCESS)){
            throw new RuntimeException(refundResponseVo.getReturn_msg());
        }
        if (!StringUtils.equals(refundResponseVo.getResult_code(),SUCCESS)){
            throw new RuntimeException(refundResponseVo.getErr_code_des());
        }
        refundOrder.setThirdOrderCode(refundResponseVo.getTransaction_id());
        refundOrder.setEndPayTime(new Date());
        refundOrder.setStatus(PayOrderStatusEnum.SUCCESS.getCode());
        payOrderService.updateById(refundOrder);
        payOrder.setStatus(PayOrderStatusEnum.REFUND.getCode());
        payOrderService.updateById(payOrder);
        payOrderInfo.setStatus(OrderStatusEnum.CLOSE.getCode());
        payOrderInfo.setOrderType(2);
        registerPayOrderService.updateById(payOrderInfo);
        OrderTypeEnum orderType = OrderTypeEnum.getInstance(3);
        WxUserinfo wxUserinfo = wxUserinfoService.getById(payOrderInfo.getWxUserinfoId());
        Doctor doctor = doctorService.getById(payOrderInfo.getDoctorId());
        DoctorWxUserinfoRelation relation = doctorWxUserinfoRelationService.getByDoctorAndOpenid(doctor.getId(),wxUserinfo.getOpenid());
        if (orderType.equals(OrderTypeEnum.VIDEO)){
            relation.setVideoNumber(0);
        }else{
            DoctorConfig doctorConfig = doctorConfigService.getByDoctorId(doctor.getId(),false);
            relation.setTextNumber(doctorConfig.getFreeMessageCount());
        }
        doctorWxUserinfoRelationService.updateById(relation);

        try {

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("orderId",payOrderInfo.getOrderCode());
            jsonObject.put("amount",payOrderInfo.getAmount());
            DySmsHelper.sendSms(doctor.getMobile(),jsonObject, DySmsEnum.REFUND);
        } catch (Exception e) {
            log.error("发送退款短信失败,doctorId{},orderId:{}",payOrderInfo.getDoctorId(),payOrderInfo.getOrderCode(),e);
        }
        return ResultMapper.ok();
    }
}
